<?php
/**
 *------------------------------------------------
 * Author: YYT[QQ:375776626]
 *------------------------------------------------
 */

abstract class Db
{
    protected $config = array(
        'db_type'          => 'mysql',
        'db_dsn'           => '',
        'db_host'          => 'localhost',
        'db_port'          => 3306,
        'db_name'          => '',
        'db_user'          => 'root',
        'db_password'      => '',
        'db_charset'       => 'UTF8',
        'db_long_connect'  => true,
        'db_prefix'        => '',
        'db_prefix_var'    => '@_',
    );

    private function _initConfig()
    {
        $this->config = array_merge($this->config, Web::config());
        if (!empty($this->config['db_dsn']) && empty($this->config['db_type'])) {
            $array = explode(':', $this->config['db_dsn']);
            $this->config['db_type'] = array_shift($array);
        }
    }

    protected function __construct()
    {
        $this->_initConfig();
    }

    protected $table;

    public function setTable($table)
    {
        $this->_initConfig();
        $this->table = trim($table) ? $this->config['db_prefix'].$table : '';
    }

    protected static $countQuery = 0;

    public static function getCountQuery()
    {
        return self::$countQuery;
    }

    public function getType()
    {
        return $this->config['db_type'];
    }

    public function parse($type, $params)
    {
        switch ($type) {
            case 'prefix':
                return str_replace($this->config['db_prefix_var'], $this->config['db_prefix'], $params);
            case 'field':
                if (is_array($params)) {
                    if (!$params) return '*';
                    $fields = array();
                    foreach ($params as $field) {
                        $field = trim($field);
                        if ($field != '*') $field = '`'.$field.'`';
                        $fields[] = $field;
                    }
                    return join(',', $fields);
                } else {
                    return trim($params) ? $params : '*';
                }
            case 'group':
                return trim($params) ? ' GROUP BY '.$params : '';
            case 'order':
                return trim($params) ? ' ORDER BY '.$params : '';
            case 'limit':
                return trim($params) ? ' LIMIT '.$params : '';
            case 'join':
                if (is_array($params)) {
                    $join  = join(' LEFT JOIN ', $params);
                    return ' LEFT JOIN '.$this->parse('prefix', $join);
                }
                break;
            case 'where':
                if (!$params) return;
                if (!is_array($params)) return ' WHERE '.$params;
                $space = ' AND ';
                if (isset($params['_space'])) {
                    $params['_space'] = strtoupper($params['_space']);
                    if ($params['_space'] == 'OR') $space = ' '.$params['_space'].' ';
                    unset($params['_space']);
                }
                $result = ' WHERE ';
                if (is_array($params)) {
                    foreach ($params as $key => $value) {
                        if (is_array($value)) { //eg: array('id' => array(1,2,3))
                            $in = join('\',\'', array_map(array($this, 'escapeString'), $value));
                            $in = trim($in, ',');
                            $result .= $key.' IN(\''.$in.'\')';
                        //条件key中带 > < LIKE 等逻辑符号
                        //eg: array('add_time >'=>'2010-10-1')
                        //eg: array('username LIKE' => '%[_%]%'); LIKE 条件用[]包括将自动转义通用匹配符号%_
                        } else if (strpos($key, ' ')) {
                            $ESCAPE = false;
                            if (strpos(strtolower($key), 'like')) {
                                preg_match_all('/\[(.*)\]/iD', $value, $match);
                                if (!empty($match[1][0])) {
                                    $replace = str_replace('%', '/%', str_replace('_', '/_', $match[1][0]));
                                    $value = preg_replace("/\[(.*)\]/iD", $replace, $value);
                                    if (strpos($value, '/')) $ESCAPE = true;
                                }
                            }
                            $result .= $key.' \''.$this->escapeString($value).'\'';
                            if ($ESCAPE) $result .= " ESCAPE '/'";
                        } else {
                            $result .= $key.' = \''.$this->escapeString($value).'\'';
                        }
                        $result .= ' '.$space.' ';
                    }
                    $result = rtrim($result, $space.' ');
                } else {
                    return $result.$params;
                }
                return $result;
        }
    }

    private $_args = array('field' => '', 'where' => '', 'group' => '', 'order' => '', 'limit' => '', 'alias' => '', 'join' => '');

    public function __call($method, $args)
    {
        $method = strtolower($method);
        if (array_key_exists($method, $this->_args)) {
            if ($method == 'join') {
                $this->_args['join'][] = $args[0];
            } else {
                $this->_args[$method] = $args[0];
            }
        } else {
            throw new Exception(__METHOD__.'调用'.get_class($this).'::'.$method.'不存在');
        }
        return $this;
    }

    public function getArgs()
    {
        $args = $this->_args;
        $this->_args = array('field' => '', 'where' => '', 'group' => '', 'order' => '', 'limit' => '', 'alias' => '', 'join' => '');
        return $args;
    }

    public static function realTable($table)
    {
        $str = stristr(trim($table), ' ');
        if ($str) return str_replace($str, '', $table);
        return $table;
    }

    public function getTable($args)
    {
        $this->table = self::realTable($this->table);
        if (Web::config('debug')) {
            $fields = $this->getFields();
            if ($fields) Web::debug('表<b>'.$this->table.'</b>结构: '.join(',', $fields), 2);
        }
        if (trim($args['alias'])) $this->table .= ' '.$args['alias'];
        return $this->table;
    }

    public function fetch()
    {
        $args = $this->getArgs();
        $table = $this->getTable($args);
        $field = $this->parse('field', $args['field']);
        $where = $this->parse('where', $args['where']);
        $group = $this->parse('group', $args['group']);
        $join = $this->parse('join', $args['join']);
        $order = $this->parse('order', $args['order']);
        $sql = 'SELECT '.$field.' FROM '.$table.$join.$where.$group.$order.' LIMIT 1';
        return $this->query($sql, __FUNCTION__);
    }

    public function fetchAll()
    {
        $args = $this->getArgs();
        $table = $this->getTable($args);
        $field = $this->parse('field', $args['field']);
        $where = $this->parse('where', $args['where']);
        $group = $this->parse('group', $args['group']);
        $join = $this->parse('join', $args['join']);
        $order = $this->parse('order', $args['order']);
        $limit = $this->parse('limit', $args['limit']);
        $sql = 'SELECT '.$field.' FROM '.$table.$join.$where.$group.$order.$limit;
        return $this->query($sql, __FUNCTION__);
    }

    public function count($function = 'count')
    {
        $args = $this->getArgs();
        $table = $this->getTable($args);
        $field = $this->parse('field', $args['field']);
        if (!$field) $field = '*';
        $where = $this->parse('where', $args['where']);
        $join = $this->parse('join', $args['join']);
        $sql = 'SELECT '.strtoupper($function).'('.$field.') FROM '.$table.$join.$where;
        $result = $this->query($sql, __FUNCTION__);
        return is_numeric($result) ? $result : 0;
    }

    public function filterData($data, $table = '')
    {
        $result = array();
        if (is_array($data)) {
            $table = trim($table) ? $table : $this->table;
            $fields = $this->getFields(str_replace($this->config['db_prefix'], '', $table));
            foreach ($data as $field => $value) {
                if (in_array(strtolower($field), $fields)) $result[$field] = $value;
            }
        }
        return $result;
    }

    public function insert($data)
    {
        $args = $this->getArgs();
        $table = $this->getTable($args);
        $data = $this->filterData($data, $table);
        $fields = $values = array();
        foreach ($data as $field => $value) {
            $fields[] = '`'.$field.'`';
            $values[] = '\''.$this->escapeString($value).'\'';
        }
        $field = $fields ? join(',', $fields) : '';
        $value = $values ? join(',', $values) : '';
        if (!$field) return 0; //如果字段全部为空就直接返回
        $sql = 'INSERT INTO '.$table.' ('.$field.') VALUES ('.$value.')';
        return $this->query($sql, __FUNCTION__);
    }

    public function update($data)
    {
        $args = $this->getArgs();
        $table = $this->getTable($args);
        $where = $this->parse('where', $args['where']);
        if (!$where) return 0; //如果不存在修改条件直接返回
        $value = '';
        if (is_array($data)) {
            $data = $this->filterData($data, $table);
            $values = array();
            foreach ($data as $field => $value) {
                $values[] = '`'.$field.'` = \''.$this->escapeString($value).'\'';
            }
            if ($values) $value = join(',', $values);
        } else {
            $value = $data;
        }
        $sql = 'UPDATE '.$table.' SET '.$value.$where;
        return $this->query($sql, __FUNCTION__);
    }

    public function delete($null = '')
    {
        $args = $this->getArgs();
        $table = $this->getTable($args);
        $sql = 'DELETE FROM '.$table;
        if (!is_null($null)) {
            $where = $this->parse('where', $args['where']);
            $limit = $this->parse('limit', $args['limit']);
            if (!$where && !$limit) return 0; //如果不存在删除条件直接返回
            $order = $this->parse('order', $args['order']);
            $sql .= $where.$order.$limit;
        }
        return $this->query($sql, __FUNCTION__);
    }

    abstract public function escapeString($string);
    abstract public function query($sql, $method = '');
    abstract public function getFields();
    abstract public function beginTransaction();
    abstract public function commit();
    abstract public function rollBack();
    abstract public function lastInsertId();
    abstract public function getVersion();

    public static function getInstance()
    {
        throw new Exception(__METHOD__.' [继承类需要定义: getInstance()]');
    }
}